#include <stdio.h>
#include <time.h>
#include <omp.h>
#include <math.h>
#include <iostream>
void testcore(int index)
{
	int sum=1;
	//printf("index:%d\n",index);
	for(int i=0;i<10000000;i++)
		sum=sum+((i*i+100)/sum);
		
}
/*
控制線程的數量
 使用編譯指令
	#pragma omp parallel num_threads(8)
 使用代碼api
	omp_set_num_threads(8);
 使用外部環境變量
    	export OMP_NUM_THREADS=8
*/
void test1()
{
	//#pragma omp parallel

	//num_threads參數控制線程的數量
	#pragma omp parallel num_threads(8)
	printf("hello\n");
}
/**
for循環優化
#pragma omp parallel for
編譯指示可以帮助将for循环工作负载划分到多个线程中，每个线程在不同的核心上运行，这显著减少了总的计算时间

omp_get_wtime从一些任意的但是一致的点返回已用去的时间，以秒为单位。因此，omp_get_wtime()-wall_timer将返回观察到的所用时间，clock()系统调用用于预估整个程序的处理器使用时间，也就是将各个特定于线程的处理器使用时间相加，然后报告最终的结果。

#pragma omp parallel for private(i) num_threads(nthreads);
private(i)指定i为线程本地存储

*/
void test2()
{
	clock_t t1=clock();
	for(int i=0;i<16;i++)
		testcore(i);
	clock_t t2=clock();
	std::cout<<"time1:"<<(double)(t2-t1)/CLOCKS_PER_SEC<<std::endl;

	
	t1=clock();
	#pragma omp parallel for
	for(int i=0;i<16;i++)
		testcore(i);
	t2=clock();
	std::cout<<"time2:"<<(double)(t2-t1)/CLOCKS_PER_SEC<<std::endl;
	
	/*
	clock()返回程序的处理器使用时间
	omp_get_wtime()返回的是墙上时间（观察时间）
	*/
	int j;
	for(int nthreads=1;nthreads<8;nthreads++){
		clock_t clock_timer=clock();
		double wall_timer=omp_get_wtime();
		#pragma omp parallel for private(j) num_threads(nthreads)
		for(j=0;j<16;j++)
			testcore(j);
		std::cout<<"threads:"<<nthreads<<" time on clock():"<<(double)(clock()-clock_timer)/CLOCKS_PER_SEC<<" time on wall:"<< omp_get_wtime()-wall_timer<<std::endl;
	}
}

/*
* 测试临界区段
  使用#pragma omp critical (name){//每次只有一个线程能够运行在这里运行的代码}
  例如：
	#pragma omp critical (section1){
		myhashtable.insert("key1","value1");
	}
	#pragma omp critical (section1){
		myhashtable.insert("key2","value2");
	}
	不会出现同时插入myhashtable的情况

  也可以使用OpenMP自己的锁和互斥
	omp_lock_t lock;
	omp_init_lock(&lock);
	omp_set_lock(&lock);
	omp_test_lock(&lock);
	omp_unset_lock(&lock);
	omp_destroy_lock(&lock);

   还可以使用嵌套锁：
	omp_nest_lock_t lock;
	omp_init_nest_lock(&lock);
	omp_set_nest_lock(&lock);
	omp_test_nest_lock(&lock);
	omp_unset_nest_lock(&lock);
	omp_unset_nest_lock(&lock);
*/

/*
* 测试执行粒度控制
  可以对代码块中的代码进行一步分类，然后由选定的线程执行它
*/
void test3(){
	
	//pragma omp sections后面的代码通过section进一步被分为各个子区段，由一个单独的线程执行。	但区段中的各个的代码循序执行
	#pragma omp parallel
	{
		std::cout<<"All threads run this\n";
		#pragma omp sections
		{
			#pragma omp section
			{	
				testcore(0);
				printf("section1\n");
			}
			#pragma omp section
			{
				testcore(1);
				printf("section2\n");
			}
			#pragma omp section
			{
				testcore(2);
				printf("section3\n");
			}
		}
	}
}

/*
理解与并行循环一起使用的firstprivate和lastprivate。注意和private的区别
*/
void test4()
{
	/*
	使用与主线程不同步的线程本地变量,可以认为是线程自己的变量，和主线程里声明的变量没有关系
	*/
	int idx1=100;
	#pragma omp parallel private(idx1)
	{
		printf("private in thread %d idx=%d\n",omp_get_thread_num(),idx1);
	}
	
	/*
	使用firstprivate指令初始化线程本地变量。相当于在线程内声明一个本地变量，并且使用主线程里的变量值赋值。
	firstprivate指令使用的变量是一个副本构造函数，用于从主线程的变量初始化自身。因此对你的类使用一个私有的副本构造函数肯定会产生不好的结果
	*/
	int idx2=100;
	#pragma omp parallel firstprivate(idx2)
	{
		printf("firstprivate in thread %d idx=%d\n",omp_get_thread_num(),idx2);
	}
	/*
	将使用最后一次循环计数生成的数据来同步主线程的数据。
	若没有lastprivate，main_var的值为最后运行的线程中的idx3的值。不一定是121
	*/
	int idx3=100;
	int main_var=2120;
	#pragma omp parallel for private(idx3) lastprivate(main_var)
	for(idx3=0;idx3<12;idx3++)
	{
		main_var=idx3*idx3;
		printf("private in thread %d idx=%d main_var=%d\n",omp_get_thread_num(),idx3,main_var);
	}
	printf("back in main thread with main_var=%d\n",main_var);
}
/*
使用OpenMP实现merge sort

*/
void test5()
{
	
}
int main(int argc,char **argv)
{
	int corenum=omp_get_num_procs();//获取处理器个数
	test4();
}


